package com.berchen.Lambda表达式;

import org.junit.Test;

import java.util.*;

/**
 * Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码（将代码像数据一样传递）。
 * <p>
 * Lambda基础语法：Java8中引入了新的操作符。“->” 该操作符称为箭头操作符或者是Lambda操作符。
 * -> 操作符将Lambda表达式拆分成两部分：
 * 左侧：Lambda表达式的参数列表。
 * 参数列表对应的是接口中方法的参数列表。
 * 完整写法：(String m,String n)
 * 可省略：
 * 1、当只有一个参数时()可以省略。
 * 2、参数的类型可以省略。因为JVM编译器通过上下文推断出数据类型，即“类型推断”。
 * 右侧：Lambda表达式中所需要执行的功能。称为 Lambda体。
 * 功能：接口中抽象方法需要实现的功能。
 * 完整写法：{System.out.println();}
 * 可省略：
 * 1、当方法的实现只有一条语句时{} ; 可以省略。
 * 2、当方法的实现只有一条返回语句时，{} ; return可以省略（并且同时省略）。
 * <p>
 * Lambda需要【函数式接口】支持：函数式接口就是接口中只有一个抽象方法的接口。
 * 可以使用一个注解@FunctionalInterface修饰。可以检查接口是否是函数式接口。
 * <p>
 * Java1.7 与Java 8的一些区别：
 * <p>
 * 在jdk1.7之前，局部内部类中使用了局部变量，那么该局部变量必须要final（需要我们手动添加final）。在8之后，不需要我们手动添加final。默认就是final了。
 * <p>
 * 类型推断：
 * public void show(Map<String,Integer > map){}
 * public void test(){
 * <p>
 * // 在Java1.7 这里必须将<>中的参数类型写清楚，否则编译不通过。但是在Java8之后<>里面的参数类型可以不用写，通过类型推断获取。
 * show(new HashMap<String,Integer>());
 * }
 */
public class TestLambda {

    public static void main(String[] args) {

        int num = 10; // 在jdk1.7之前，局部内部类中使用了局部变量，那么该局部变量必须要final（需要我们手动添加final）。在8之后，不需要我们手动添加final。默认就是final了。
        // ()-> System.out.println("chen") 就是一个Lambda表达式。
        new Thread(() -> System.out.println("chen" + num)).start();
    }


    @Test
    public void test1() {

        // 利用匿名内部类
        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        TreeSet<Integer> ts = new TreeSet<>(com);

        // 利用Lambda表达式：(x,y)->Integer.compare(x,y)就是一个Lambda表达式。
        Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
        TreeSet<Integer> ts1 = new TreeSet<>(com1);
    }

    @Test
    public void test2() {

        // 需求：获取当先公司中员工年龄大于35的员工信息
        List<Employee> employees = Arrays.asList(

                new Employee("张三", 18, 999.99),
                new Employee("李四", 19, 999.99),
                new Employee("王五", 120, 999.99),
                new Employee("田七", 10, 999.99)
        );
        // 原始方式
        List<Employee> emps1 = filterEmployees(employees);

        // 优化方式一
        // 这里第二个参数传递的是什么实现类，那么筛选条件就是哪一个。
        List<Employee> emps2 = filterEmployee(employees, new FilterEmployeeByAge());

        // 优化方式二：匿名内部类
        List<Employee> emps3 = filterEmployee(employees, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {

                // 年龄大于20为条件
                return employee.getAge() > 20;
            }
        });

        // 优化方式三：Lambda表达式：emp->emp.getAge()>20 emp 就是test方法的参数，emp.getAge()>20.就是return语句。只是将return 省略了。
        List<Employee> emps4 = filterEmployee(employees, emp -> emp.getAge() > 10);

        // 方法引用
        emps4.forEach(System.out::println);
        emps4.forEach(i -> System.out.println(i));
//        for(Employee emp:emps){
//            System.out.println(emp.getAge());
//        }
    }

    // 原始方式
    public List<Employee> filterEmployees(List<Employee> employees) {

        List<Employee> emps = new ArrayList<>();
        for (Employee employee : employees) {

            // 以年龄大于35的，为条件，来获取员工信息。
            // 如果条件改变，那么这个方法的代码不会改变，还是只改变这里的判断条件。
            // 这样写代码的重复性就太高了。（代码冗余）
            // 下面对代码进行优化
            if (employee.getAge() > 35) {
                emps.add(employee);
            }
        }
        return emps;
    }

    // 优化方式一：策略模式
    public List<Employee> filterEmployee(List<Employee> employees, MyPredicate<Employee> mp) {
        List<Employee> emps = new ArrayList<>();
        for (Employee employee : employees) {

            // 这里使用的就是MyPredicate的test方法进行判断。具体的判断条件在MyPredicate的实现类中。但是以后要筛选人员这个方法就一直是这样，就不需要改变了。
            if (mp.test(employee)) {
                emps.add(employee);
            }
        }
        return emps;
    }
}
